package com.android.powerbankpad.posutils;

import android.annotation.SuppressLint;
import android.content.Context;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.powerbankpad.api.ApiUrl;
import com.android.powerbankpad.config.Constants;
import com.android.powerbankpad.network.CommonalityModel;
import com.android.powerbankpad.network.NetWorkListener;
import com.android.powerbankpad.network.okHttpModel;
import com.android.powerbankpad.stripe.TokenProvider;
import com.stripe.stripeterminal.Terminal;
import com.stripe.stripeterminal.external.callable.Callback;
import com.stripe.stripeterminal.external.callable.Cancelable;
import com.stripe.stripeterminal.external.callable.DiscoveryListener;
import com.stripe.stripeterminal.external.callable.PaymentIntentCallback;
import com.stripe.stripeterminal.external.callable.ReaderCallback;
import com.stripe.stripeterminal.external.callable.ReaderListener;
import com.stripe.stripeterminal.external.callable.ReaderReconnectionListener;
import com.stripe.stripeterminal.external.callable.TerminalListener;
import com.stripe.stripeterminal.external.models.BatteryStatus;
import com.stripe.stripeterminal.external.models.CollectConfiguration;
import com.stripe.stripeterminal.external.models.ConnectionConfiguration;
import com.stripe.stripeterminal.external.models.ConnectionStatus;
import com.stripe.stripeterminal.external.models.DiscoveryConfiguration;
import com.stripe.stripeterminal.external.models.PaymentIntent;
import com.stripe.stripeterminal.external.models.PaymentStatus;
import com.stripe.stripeterminal.external.models.Reader;
import com.stripe.stripeterminal.external.models.ReaderDisplayMessage;
import com.stripe.stripeterminal.external.models.ReaderInputOptions;
import com.stripe.stripeterminal.external.models.ReaderSoftwareUpdate;
import com.stripe.stripeterminal.external.models.TerminalException;
import com.stripe.stripeterminal.log.LogLevel;
import com.ws.universal.tools.countdown.CommitTimer;
import com.ws.universal.tools.utils.CacheUtils;
import com.ws.universal.tools.utils.LogcatUtils;
import com.ws.universal.tools.utils.PriceUtils;
import com.ws.universal.tools.utils.StringUtils;
import com.ws.universal.tools.utils.ToastUtils;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nonnull;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;

public class StripePosUtils implements NetWorkListener {
    private TokenProvider tokenProvider;
    private DiscoveryConfiguration discoveryConfiguration;
    private Disposable createStripe;
    private Cancelable cancelable;
    private Reader readersDevice;
    private String connectLocationId;

    private PosListener posListener;


    private Context context;
    private Timer timerLoadData; //根据时间点轮询加载广告数据

    public void initPos(Context context) {
        this.context = context;


        getPosSecret();


    }

    public void setPosListener(PosListener posListener) {
        this.posListener = posListener;
    }

    public void getPosSecret() {
        String baseUrl = CacheUtils.getString(Constants.base_url) + "/app/Thunderlinkpos/getPosClientSecret";
        okHttpModel.post(baseUrl, null, ApiUrl.getPosClientSecret_Id, this);
    }

    private void getConnectPosId(String pos_number) {
        String baseUrl = CacheUtils.getString(Constants.base_url) + "/app/Thunderlinkpos/getLocations";
        Map<String, String> params = new HashMap<>();
        params.put("pos_number", pos_number);
        okHttpModel.post(baseUrl, params, ApiUrl.getLocations_Id, this);
    }


    //创建付款意向
    private void createStripeOrder() {
        String baseUrl = CacheUtils.getString(Constants.base_url) + "/app/Thunderlinkpos/createPaymentIntent";
        Map<String, String> params = new HashMap<>();
        params.put("device_code", CacheUtils.getString(Constants.serial_number));
        okHttpModel.post(baseUrl, params, ApiUrl.createPaymentIntent_Id, this);
    }


    @SuppressLint("MissingPermission")
    public void discoverReadersAction() {
        LogcatUtils.e("StripePosUtils 扫描设备 ");
        discoveryConfiguration = new DiscoveryConfiguration.UsbDiscoveryConfiguration(120, false);
        Cancelable discoverCancelable = Terminal.getInstance().discoverReaders(discoveryConfiguration, new DiscoveryListener() {
            @Override
            public void onUpdateDiscoveredReaders(@NonNull List<Reader> readers) {
                LogcatUtils.e("StripePosUtils  发现设备 " + readers.size());
                LogcatUtils.e("StripePosUtils  发现设备 readers.size " + readers);
                for (int i = 0; i < readers.size(); i++) {
                    LogcatUtils.e("StripePosUtils  发现设备 readers getSerialNumber " + readers.get(i).getSerialNumber());
                    if (readers.get(i).getSerialNumber().indexOf("WPC") > -1) {
                        readersDevice = readers.get(i);
                        getConnectPosId(readersDevice.getSerialNumber());
                        return;
                    }
                }
            }
        }, new Callback() {
            @Override
            public void onSuccess() {
                LogcatUtils.e("StripePosUtils  discoverReaders 扫描设备 成功 ");
            }

            @Override
            public void onFailure(@NonNull TerminalException e) {
                LogcatUtils.e("StripePosUtils  discoverReaders 扫描失败 e " + e);
                LogcatUtils.e("StripePosUtils  discoverReaders 扫描失败 e " + e.getErrorMessage());
                Observable.timer(30000L, TimeUnit.MILLISECONDS).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Long>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                    }

                    @Override
                    public void onNext(Long aLong) {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {
                        discoverReadersAction();
                    }
                });

                LogcatUtils.e("StripePosUtils  discoverReaders 扫描设备 失败  " + e);
            }


        });

    }


    private void connectDevicePos(Reader selectedReader, String connectLocationId) {
        LogcatUtils.e(" StripePosUtils 执行 connectPos ");
        ConnectionConfiguration.UsbConnectionConfiguration usbConnectionConfiguration = new ConnectionConfiguration.UsbConnectionConfiguration(connectLocationId, true, readerReconnectionListener);
        Terminal.getInstance().connectUsbReader(selectedReader, usbConnectionConfiguration, readerListener, new ReaderCallback() {

            @Override
            public void onSuccess(@Nonnull Reader reader) {
                //stopProgressDialog();
                ToastUtils.showShort("pos initialization", 5000);
                createStripeOrder();
                LogcatUtils.e("StripePosUtils  stopProgressDialog connectPos 连接读卡器 Successfully");
                if (posListener != null) {
                    posListener.connectDevice();
                }

            }

            @Override
            public void onFailure(@Nonnull TerminalException e) {
                LogcatUtils.e("StripePosUtils connectPos 连接读卡器 onFailure e " + e);
                LogcatUtils.e("StripePosUtils connectPos 连接读卡器 onFailure e " + e.getErrorMessage());
            }
        });
    }


    private ReaderReconnectionListener readerReconnectionListener = new ReaderReconnectionListener() {
        @Override
        public void onReaderReconnectStarted(@NonNull Reader reader, @NonNull Cancelable cancelReconnect) {
            // 1. 在重新连接尝试开始时收到通知
            // 使用cancelable随时停止重新连接
            LogcatUtils.e("StripePosUtils 重新连接尝试 ");

        }

        @Override
        public void onReaderReconnectSucceeded(@NonNull Reader reader) {
            // 2. 读卡器重新连接成功时通知
            // 应用程序现已连接
            LogcatUtils.e("StripePosUtils 重新连接 成功  ");
            createStripeOrder();
        }

        @Override
        public void onReaderReconnectFailed(@NonNull Reader reader) {
            // 3. 当读卡器重新连接失败时通知
            // 应用程序现已断开连接
            LogcatUtils.e("StripePosUtils 重新连接 失败  ");
        }
    };


    public void stripeOnDestroy() {
        cancelable.cancel(cancelabaCallback);
        Terminal.getInstance().disconnectReader(new Callback() {
            @Override
            public void onSuccess() {
                LogcatUtils.e("StripePosUtils 断开连接成功 ");

            }

            @Override
            public void onFailure(@NonNull TerminalException e) {

            }
        });
    }


    private ReaderListener readerListener = new ReaderListener() {
        @Override
        public void onBatteryLevelUpdate(float batteryLevel, @NonNull BatteryStatus batteryStatus, boolean isCharging) {
            ReaderListener.super.onBatteryLevelUpdate(batteryLevel, batteryStatus, isCharging);
            LogcatUtils.e(" StripePosUtils readerListener onBatteryLevelUpdate 电池电量更新 batteryLevel " + batteryLevel + " batteryStatus " + batteryStatus + " isCharging " + isCharging);

        }

        @Override
        public void onFinishInstallingUpdate(@Nullable ReaderSoftwareUpdate update, @Nullable TerminalException e) {
            ReaderListener.super.onFinishInstallingUpdate(update, e);
            LogcatUtils.e(" StripePosUtils readerListener onFinishInstallingUpdate  完成安装更新 ");
        }

        @Override
        public void onReportAvailableUpdate(@NonNull ReaderSoftwareUpdate update) {
            ReaderListener.super.onReportAvailableUpdate(update);
            LogcatUtils.e(" StripePosUtils readerListener onReportAvailableUpdate 报告可用更新 ");
        }

        @Override
        public void onReportLowBatteryWarning() {
            ReaderListener.super.onReportLowBatteryWarning();
            LogcatUtils.e(" StripePosUtils readerListener onReportLowBatteryWarning 报告低电量警告 ");
        }

        @Override
        public void onReportReaderSoftwareUpdateProgress(float progress) {
            ReaderListener.super.onReportReaderSoftwareUpdateProgress(progress);
            LogcatUtils.e(" StripePosUtils readerListener onReportReaderSoftwareUpdateProgress 报告阅读器软件更新进度 progress " + progress);
            //LogcatUtils.e(" StripePosUtils readerListener onReportReaderSoftwareUpdateProgress 报告阅读器软件更新进度 progress "+progress+" 百分比" + PriceUtils.getPrice((float) (progress/1.0*100)) +" % ");
            if (posListener != null) {
                posListener.onReportReaderSoftwareUpdateProgress("pos  Update " + PriceUtils.getPrice((float) (progress / 1.0 * 100)) + " % ");
            }

        }

        @Override
        public void onRequestReaderDisplayMessage(@NonNull ReaderDisplayMessage message) {
            ReaderListener.super.onRequestReaderDisplayMessage(message);
            LogcatUtils.e(" StripePosUtils readerListener onRequestReaderDisplayMessage 根据请求阅读器显示消息 ");
        }

        @Override
        public void onRequestReaderInput(@NonNull ReaderInputOptions options) {
            ReaderListener.super.onRequestReaderInput(options);
            LogcatUtils.e(" StripePosUtils readerListener onRequestReaderInput 根据请求读者输入 ");

        }

        @Override
        public void onStartInstallingUpdate(@NonNull ReaderSoftwareUpdate update, @Nullable Cancelable cancelable) {
            ReaderListener.super.onStartInstallingUpdate(update, cancelable);
            if (posListener != null) {
                posListener.onStartInstallingUpdate("pos Start Installing Update ");
            }
            LogcatUtils.e(" StripePosUtils readerListener onStartInstallingUpdate 开始安装更新 ");
        }
    };

    private TerminalListener listener = new TerminalListener() {
        @Override
        public void onUnexpectedReaderDisconnect(@NonNull Reader reader) {
            LogcatUtils.e("StripePosUtils 读卡器意外断开 getSerialNumber " + reader.getSerialNumber());
        }

        @Override
        public void onConnectionStatusChange(@NonNull ConnectionStatus status) {
            //TerminalListener.super.onConnectionStatusChange(status);
            LogcatUtils.e("StripePosUtils onConnectionStatusChange 连接状态 " + status);
            if (status == ConnectionStatus.NOT_CONNECTED) {
                getPosSecret();
            }


        }

        @Override
        public void onPaymentStatusChange(@NonNull PaymentStatus status) {
            //TerminalListener.super.onPaymentStatusChange(status);
            LogcatUtils.e("StripePosUtils onPaymentStatusChange 付款状态  " + status);

        }
    };


    /**
     * 创建付款意向
     *
     * @param clientSecret
     */
    private void paymentIntention(String clientSecret) {
        Terminal.getInstance().retrievePaymentIntent(clientSecret, createpaymentIntentCallBack);


    }

    public String payment_intent_id = "";
    private PaymentIntentCallback createpaymentIntentCallBack = new PaymentIntentCallback() {
        @Override
        public void onSuccess(@NonNull PaymentIntent paymentIntent) {
            payment_intent_id = paymentIntent.getId();
            LogcatUtils.e("StripePosUtils 创建付款意向 成功 payment_intent_id  " + payment_intent_id);
            collectPaymentMethods(paymentIntent);

            countDownTime();
        }

        @Override
        public void onFailure(@NonNull TerminalException exception) {
            LogcatUtils.e("StripePosUtils 创建付款意向 失败  " + exception);
            LogcatUtils.e("StripePosUtils 创建付款意向 失败  " + exception.getErrorMessage());
            createStripeOrder();

        }
    };

    private void countDownTime() {
        if (timerLoadData != null) {
            timerLoadData.purge();
            timerLoadData.cancel();
            timerLoadData = null;
        }
        if (timerLoadData == null) {
            timerLoadData = new Timer();
        }
        timerLoadData.schedule(new CommitTimer() {
            @Override
            public void run() {
                LogcatUtils.e("StripePosUtils 倒计时结束 ");
                cancelable.cancel(cancelabaCallback);
            }


        }, 40 * 60 * 1000, 40 * 60 * 1000);////Teask，延时时间，轮询间隔 //延时时间如果传0 会立马执行一次
    }


    /**
     * 收集付款方式
     *
     * @param paymen
     */


    private void collectPaymentMethods(PaymentIntent paymen) {


        CollectConfiguration collectConfig = new CollectConfiguration.Builder().updatePaymentIntent(true).build();
        cancelable = Terminal.getInstance().collectPaymentMethod(paymen, collectPaymentMethodsCallback, collectConfig);


    }


    private PaymentIntentCallback collectPaymentMethodsCallback = new PaymentIntentCallback() {
        @Override
        public void onSuccess(@NonNull PaymentIntent paymentIntent) {
            LogcatUtils.e("StripePosUtils 收集付款方式 成功 " + paymentIntent.toString());
            if (posListener != null) {
                posListener.readCard();
            }
            if (timerLoadData != null) {
                timerLoadData.purge();
                timerLoadData.cancel();
                timerLoadData = null;
            }
            confirmPayment(paymentIntent);
        }

        @Override
        public void onFailure(@NonNull TerminalException exception) {
            // Placeholder for handling exception
            LogcatUtils.e("StripePosUtils 收集付款方式 失败 " + exception);
            LogcatUtils.e("StripePosUtils 收集付款方式 失败 " + exception.getErrorMessage());


        }
    };


    private Callback cancelabaCallback = new Callback() {
        @Override
        public void onSuccess() {
            LogcatUtils.e("StripePosUtils 取消成功");
            createStripeOrder();
        }

        @Override
        public void onFailure(@NonNull TerminalException e) {
            LogcatUtils.e("StripePosUtils 取消款 失败 e " + e);
            LogcatUtils.e("StripePosUtils 取消 失败 e " + e.getErrorMessage());
        }
    };


    /**
     * 确认付款
     *
     * @param intent
     */
    private void confirmPayment(PaymentIntent intent) {
        if (intent.getPaymentMethod().getInteracPresentDetails() != null) {
            ToastUtils.showShort("This card type is not supported", 5000);
            cancelable.cancel(cancelabaCallback);
            return;
        }
        LogcatUtils.e("StripePosUtils 确认付款成功  intent " + intent.getPaymentMethod().getCardPresentDetails().toString());
        Terminal.getInstance().confirmPaymentIntent(intent, new PaymentIntentCallback() {
            @Override
            public void onSuccess(@NonNull PaymentIntent paymentIntent) {
                LogcatUtils.e("StripePosUtils 确认付款成功 paymentIntent " + paymentIntent.toString());
                String baseUrl = CacheUtils.getString(Constants.base_url) + "/app/Thunderlinkpos/register";

                Map<String, String> params = new HashMap<>();
                if (intent.getPaymentMethod().getCardPresentDetails() != null) {
                    params.put("openid", StringUtils.getString(intent.getPaymentMethod().getCardPresentDetails().getExpYear(),
                            intent.getPaymentMethod().getCardPresentDetails().getExpMonth(), intent.getPaymentMethod().getCardPresentDetails().getLast4()));
                }
                params.put("stripe_pay_id", paymentIntent.getId());
                params.put("device_code", CacheUtils.getString(Constants.serial_number));
                params.put("is_credit", "true");
                okHttpModel.post(baseUrl, params, ApiUrl.register_Id, StripePosUtils.this);
            }

            @Override
            public void onFailure(@NonNull TerminalException e) {
                LogcatUtils.e("StripePosUtils 确认付款失败 onFailure e  " + e);
                LogcatUtils.e("StripePosUtils 确认付款失败 onFailure e  " + e.getErrorMessage());
                ToastUtils.showShort("msg " + e.getErrorMessage(), 5000);

            }
        });


    }

    @Override
    public void onSucceed(JSONObject jsonData, int id, CommonalityModel commonality) throws JSONException {
        JSONObject data = jsonData.optJSONObject("data");
        switch (id) {
            case ApiUrl.getPosClientSecret_Id:
                CacheUtils.setString("secret", data.optString("secret"));
                LogcatUtils.e("StripePosUtils 重新获取 secret " + data.optString("secret"));
                if (tokenProvider == null) {
                    tokenProvider = new TokenProvider();

                }
                //TokenProvider
                if (!Terminal.isInitialized()) {
                    try {
                        Terminal.initTerminal(context, LogLevel.VERBOSE, tokenProvider, listener);
                        LogcatUtils.e("StripePosUtils 初始化");
                        discoverReadersAction();

                    } catch (TerminalException e) {
                        ToastUtils.showShort("msg " + e.getErrorMessage(), 5000);
                        LogcatUtils.e("StripePosUtils 初始化" + e);
                    }
                } else {
                    discoverReadersAction();
                }
                break;
            case ApiUrl.getLocations_Id:
                connectLocationId = data.optString("id");
                connectDevicePos(readersDevice, connectLocationId);
                break;

            case ApiUrl.createPaymentIntent_Id:
                paymentIntention(data.optString("client_secret"));
                if (posListener != null) {
                    posListener.deviceScanSucceed(data.optString("freight_basis"));
                }

                break;
            case ApiUrl.register_Id:
                //CacheUtils.setString("token", data.optString("token"));
                if (posListener != null) {
                    posListener.transactionSuccessful();
                }
                createStripeOrder();

                break;
        }
    }

    @Override
    public void onFail(JSONObject jsonData, int id, CommonalityModel commonality) {
        ToastUtils.showShort(commonality.getMsg());
        switch (id) {
            case ApiUrl.getLocations_Id://连接设备
                getConnectPosId(readersDevice.getSerialNumber());
                break;
            case ApiUrl.register_Id: //确认付款
                stripeOnDestroy();
                break;
        }
    }

    @Override
    public void onError(int id, Exception e) {
        switch (id) {
            case ApiUrl.getLocations_Id://连接设备
                getConnectPosId(readersDevice.getSerialNumber());
                break;
            case ApiUrl.register_Id://确认付款
                stripeOnDestroy();
                break;
        }
    }

    public interface PosListener {

        //链接设备
        void connectDevice();

        //刷卡
        void readCard();

        //加载Ui
        void deviceScanSucceed(String text);

        //支付成功
        void onReportReaderSoftwareUpdateProgress(String text);

        //失败
        void onStartInstallingUpdate(String text);

        void transactionSuccessful();


    }
}
